// Copyright 2022 The Google Research Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_DNN_ACCELERATOR_HLS_SRC_ACCELTYPES_H_
#define THIRD_PARTY_DNN_ACCELERATOR_HLS_SRC_ACCELTYPES_H_

#include <systemc.h>

#include <ac_channel.h>
#include <ac_fixed.h>
#include <ac_int.h>
#include <ac_std_float.h>
#include <ac_sc.h>
#include <ac_sysc_macros.h>
#include <mc_connections.h>
#include <ccs_types.h>

#include "src/Params.h"

struct MemoryRequest {
  int address;
  int burstSize;

  static const unsigned int width = 32 + 32;

  // Used by Marshaller in Connections to convert C++ datatypes to SystemC types
  template <unsigned int Size>
  void Marshall(Marshaller<Size>& m) {
    m& address;
    m& burstSize;
  }

  inline friend void sc_trace(sc_trace_file* tf,
                              const MemoryRequest& memRequest,
                              const std::string& NAME) {
    sc_trace(tf, memRequest.address, NAME + ".address");
    sc_trace(tf, memRequest.burstSize, NAME + ".burstSize");
  }

  inline friend std::ostream& operator<<(ostream& os,
                                         const MemoryRequest& memRequest) {
    os << memRequest.address << " ";
    os << memRequest.burstSize << " ";

    return os;
  }
};

template <typename TYPE, size_t SIZE>
class Pack1D {
 public:
  TYPE value[SIZE];
  Pack1D() {}

  TYPE& operator[](unsigned int i) { return this->value[i]; }
  const TYPE& operator[](unsigned int i) const { return this->value[i]; }

  static const unsigned int width = TYPE::width * SIZE;

  template <unsigned int Size>
  void Marshall(Marshaller<Size>& m) {
#pragma hls_unroll yes
    for (unsigned int i = 0; i < SIZE; i++) {
      m& value[i];
    }
  }
};

template <typename TYPE, size_t SIZE>
inline bool operator==(const Pack1D<TYPE, SIZE>& lhs,
                       const Pack1D<TYPE, SIZE>& rhs) {
  bool is_equal = true;
#pragma hls_unroll yes
  for (unsigned i = 0; i < SIZE; i++)
    is_equal &= (lhs.value[i] == rhs.value[i]);
  return is_equal;
}

template <typename TYPE, size_t SIZE>
inline void sc_trace(sc_trace_file* tf, const Pack1D<TYPE, SIZE>& vec,
                     const std::string& NAME) {
  sc_trace(tf, vec.value, NAME);
}

template <typename TYPE, size_t SIZE>
inline std::ostream& operator<<(ostream& os, const Pack1D<TYPE, SIZE>& vec) {
  for (int i = 0; i < SIZE; i++) {
    os << vec[i] << " ";
  }
  return os;
}

template <typename TYPE, size_t SIZE>
struct mc_typedef_T_traits<Pack1D<TYPE, SIZE> > {
  enum { bitwidth = TYPE::width * SIZE, issigned = 0 };
};

template <typename TYPE, size_t SIZE, int VEC_SIZE>
inline void type_to_vector(const Pack1D<TYPE, SIZE>& in, int length,
                           sc_lv<VEC_SIZE>& rvec) {
  Marshaller<VEC_SIZE> marshaller;
  Pack1D<TYPE, SIZE> inCopy = in;
  marshaller& inCopy;
  rvec = marshaller.GetResult();
}

template <typename TYPE, size_t SIZE, int VEC_SIZE>
inline void vector_to_type(const sc_lv<VEC_SIZE>& in, bool issigned,
                           Pack1D<TYPE, SIZE>* result) {
  Marshaller<VEC_SIZE> marshaller(in);
  result->Marshall(marshaller);
}

template <typename T, int N>
struct chanStruct {
  T data[N];
};

#endif  // THIRD_PARTY_DNN_ACCELERATOR_HLS_SRC_ACCELTYPES_H_
